/*
 * Killocan 2013
 * http://killocan.blogspot.com
*/

#include <stdio.h>

#include "stage.h"

#include "tileactions.h"
#include "spritefiles.h"
#include "defines.h"
#include "globalgamestate.h"

#include "blader.h"
#include "beak.h"
#include "supercutter.h"
#include "flyingshell.h"
#include "killerbullet.h"
#include "spine.h"
#include "met.h"
#include "picketman.h"
#include "sniperjoe.h"
#include "peng.h"
#include "bigeye.h"
#include "flea.h"
#include "octopusbattery.h"
#include "screwbomber.h"
#include "bombomb.h"
#include "crazyrazy.h"
#include "moveplatform.h"

#include "hitexplosion.h"
#include "explosionlittle.h"
#include "megamanexplosion.h"

static volatile int update_scroll = 0;
static void screenscroll_timer()
{
  update_scroll = 1;
}
END_OF_FUNCTION(screenscroll_timer);

Stage::Stage(std::string stage_path, Camera & camera, Player ** player)//, std::vector<Character *> & characters_vec)
{
  cur_waypoint = 0;
  horz_scroll  = false;
  has_fg_tiles = false;

  hasMovingPlatform = false;

  load(stage_path, camera, player);//, characters_vec);

  LOCK_VARIABLE(update_scroll);
  LOCK_FUNCTION(screenscroll_timer);
  install_int(screenscroll_timer, 110);
}

Stage::~Stage()
{
  remove_int(screenscroll_timer);

  unload();
}

unsigned char Stage::tileAction(int x, int y) const
{
  //NOTE: Not supposed to happen, remove on final version.
  //if (x < 0 || y < 0) return mm_tile_actions::TILE_SOLID;

  return map[y][x].action;
}

unsigned char Stage::tileActionUnnormalized(int x, int y) const
{
  x /= mm_graphs_defs::TILE_SIZE;
  y /= mm_graphs_defs::TILE_SIZE;
  return map[y][x].action;
}

unsigned char Stage::tileNumberUnnormalized(int x, int y) const
{
  x /= mm_graphs_defs::TILE_SIZE;
  y /= mm_graphs_defs::TILE_SIZE;
  return map[y][x].tile_number;
}

int Stage::unload()
{
#if 0
  std::vector<RLE_SPRITE*>::iterator i;
  for (i = tiles.tile_img_rle.begin(); i != tiles.tile_img_rle.end(); ++i)
  {
    destroy_rle_sprite(*i);
  }
  tiles.tile_img_rle.clear();
#endif
  std::vector<BITMAP*>::iterator i;
  for (i = tiles.tile_img.begin(); i != tiles.tile_img.end(); ++i)
  {
    destroy_bitmap(*i);
  }
  tiles.tile_img.clear();

  destroy_bitmap(tileset);
  tileset = NULL;

  free(sectors);
  sectors = NULL;

  for(int i = 0; i < max_y; i++)
  {
    free(map[i]);
    map[i] = NULL;
  }

  free(map);
  map = NULL;

  std::map<unsigned int, AnimSequence *>::iterator it;
  for (it = preLoadedSprites.begin(); it != preLoadedSprites.end(); ++it)
  {
    delete it->second;
  }

  return 0;
}

#define ignore_result(x) ({ __typeof__(x) z = x; (void)sizeof z; })
int Stage::load(const std::string & stage_path, Camera & camera, Player ** player) //, std::vector<Character *> & characters_vec)
{
  FILE *fp;

  std::string stage_file = stage_path + "/stage.dat";
  if ((fp = fopen(stage_file.c_str(), "rb")) == NULL)
  {
    return -1;
  }

  //ignore_result(fread(&max_x, sizeof(int), 1, fp));
  //ignore_result(fread(&max_y, sizeof(int), 1, fp));
  //ignore_result(fread(&default_tile, sizeof(unsigned char), 1, fp));
  fread(&max_x, sizeof(int), 1, fp);
  fread(&max_y, sizeof(int), 1, fp);
  fread(&default_tile, sizeof(unsigned char), 1, fp);
  
  int sectors_num = (max_x/mm_graphs_defs::TILES_X) * (max_y/mm_graphs_defs::TILES_Y);
  sectors = (sector_t*) malloc(sizeof(sector_t) * sectors_num);
  memset(sectors, 0, sizeof(sector_t) * sectors_num);

  map = (MAP_INFO**) malloc(sizeof(MAP_INFO*) * max_y);
  for(int i = 0; i < max_y; i++)
  {
    map[i] = (MAP_INFO*) malloc(sizeof(MAP_INFO) * max_x);
    //ignore_result(fread(&(map[i][0]), sizeof(MAP_INFO), max_x, fp));
	fread(&(map[i][0]), sizeof(MAP_INFO), max_x, fp);
  }

  fclose(fp);

  bool hasPicketMan = false;
  bool hasCrazyRazy = false;

  for (int y = 0; y < max_y; ++y)
  {
    for (int x = 0; x < max_x; ++x)
    {
      if (map[y][x].action == mm_tile_actions::TILE_MEGAMAN_WAYPOINT)
      {
        int waypointX = x*mm_graphs_defs::TILE_SIZE+map[y][x].xOffset;
        waypoint_t waypoint = {waypointX, y*mm_graphs_defs::TILE_SIZE};
        waypoints.push_back(waypoint);
      }
      else if (map[y][x].action == mm_tile_actions::TILE_MAP_BEGIN)
      {
        camera.x = x*mm_graphs_defs::TILE_SIZE;
        camera.y = y*mm_graphs_defs::TILE_SIZE;
      }
      else if (map[y][x].action == mm_tile_actions::TILE_SCROLL_LIMIT)
      {
        int ydesl = y / mm_graphs_defs::TILES_Y;
        int xdesl = x / mm_graphs_defs::TILES_X;
        int sector = ydesl*(max_x/mm_graphs_defs::TILES_X)+xdesl;

        sectors[sector].scroll_forbid = true;
      }
      //else if (map[y][x].action == mm_tile_actions::TILE_FOREGROUND)
      else if (map[y][x].isForeground == true)
      {
        int ydesl = y / mm_graphs_defs::TILES_Y;
        int xdesl = x / mm_graphs_defs::TILES_X;
        int sector = ydesl*(max_x/mm_graphs_defs::TILES_X)+xdesl;

        sectors[sector].has_fg_tiles = true;
        this->has_fg_tiles = true;
      }
      else
      {
        unsigned char tile_action = map[y][x].action;
        if ((tile_action > mm_tile_actions::TILE_DEATH) && (tile_action < mm_tile_actions::TILE_TIMER))
        {
          if (tile_action == mm_tile_actions::TILE_ENEMY_PICKETMAN)
            hasPicketMan = true;
          else if (tile_action == mm_tile_actions::TILE_ENEMY_CRAZYRAZY)
            hasCrazyRazy = true;

          if (preLoadedSprites.find(tile_action) == preLoadedSprites.end())
          {
#ifdef DEBUG
            fprintf(stderr,"Preload: [%s]\n", mm_spritefiles::sprite_files[tile_action]);
#endif
            preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
              (tile_action, new AnimSequence(mm_spritefiles::sprite_files[tile_action])));
          }
        }
      }
    }
  }

#ifdef DEBUG
  fprintf(stderr,"Preload: [megaman]\n");
#endif
  // Megaman, keep a copy for weapon color change.
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::MEGAMAN_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::MEGAMAN_SPRITES], true)));

#ifdef DEBUG
  fprintf(stderr,"Preload: [bullets]\n");
#endif
  // Weapons
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::WEAPONS_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::WEAPONS_SPRITES])));
  // Iceman TODO: load bassed in megaman curr avaliable weapons
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::WEAPONS_ICEMAN, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::WEAPONS_ICEMAN])));

#ifdef DEBUG
  fprintf(stderr,"Preload: [explosion]\n");
#endif
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::HITEXPLOSION_SPRITES,     new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::HITEXPLOSION_SPRITES])));
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::EXPLOSIONLITTLE_SPRITES,  new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::EXPLOSIONLITTLE_SPRITES])));
  preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
    (mm_spritefiles::MEGAMANEXPLOSION_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::MEGAMANEXPLOSION_SPRITES])));

  if (hasPicketMan == true)
  {
#ifdef DEBUG
    fprintf(stderr,"Preload: [PicketMan Hammer]\n");
#endif
    preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
      (mm_spritefiles::PICKETMANHAMMER_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::PICKETMANHAMMER_SPRITES])));
  }

  if (hasCrazyRazy == true)
  {
#ifdef DEBUG
    fprintf(stderr,"Preload: [CrazyRazy Upper and Lower parts]\n");
#endif
    preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
      (mm_spritefiles::CRAZYRAZY_LOWER_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::CRAZYRAZY_LOWER_SPRITES])));
    preLoadedSprites.insert(std::pair<unsigned int, AnimSequence *> 
      (mm_spritefiles::CRAZYRAZY_UPPER_SPRITES, new AnimSequence(mm_spritefiles::sprite_files[mm_spritefiles::CRAZYRAZY_UPPER_SPRITES])));
  }

#ifdef DEBUG
  fprintf(stderr,"Preload OK.\n");
#endif

  *player = new Player(*this);
  Player * cur_player = m_player = *player;
  cur_player->sx = 0;
  cur_player->sy = 0;
  cur_player->x = waypoints[cur_waypoint].x;
  cur_player->y = waypoints[cur_waypoint].y;
  cur_player->old_x = cur_player->x;
  cur_player->old_y = cur_player->y;
  cur_player->h = mm_player_defs::PLAYERHEIGHT;
  cur_player->w = mm_player_defs::PLAYERWIDTH;
  cur_player->velx = mm_player_defs::VELMOVING;
  cur_player->vely = 0;
  cur_player->isFacingRight  = true;
  cur_player->isFacingDown   = false;
  cur_player->lockjump   = false;
  cur_player->grabstair  = false;
  cur_player->overstair  = false;

#if 0
  for (int y = 0; y < max_y; ++y)
  {
    for (int x = 0; x < max_x; ++x)
    {
      unsigned char tile_action = map[y][x].action;
      Character * cur_char = NULL;
      if ((tile_action > mm_tile_actions::TILE_DEATH) && (tile_action < mm_tile_actions::TILE_TIMER))
      {
        if ((cur_char = createCharacter(tile_action, x, y)) != NULL)
        {
#ifdef DEBUG
          fprintf(stderr,"Enemy add: [%s]\n", mm_spritefiles::sprite_files[tile_action]);
#endif
          characters_vec.push_back(cur_char);
        }
      }
    }
  }
#endif

#ifdef DEBUG
  fprintf(stderr,"Load weapons.\n");
#endif
  cur_player->weapons.insert(std::pair<mm_weapons::WEAPONS, int>(mm_weapons::W_MEGA_BUSTER, 100)); // +Inf :)
  if (GlobalGameState::hasPlatformGun)
  {
    cur_player->weapons.insert(std::pair<mm_weapons::WEAPONS, int>(mm_weapons::W_PLATFORM_CREATOR, 28));
  }
  for (int i = 0; i < 6; i++) // bosses.
  {
    if (GlobalGameState::enemyDefeated[i] == true)
    {
      cur_player->weapons.insert(std::pair<mm_weapons::WEAPONS, int>((mm_weapons::WEAPONS)(i + 2), 28));
    }
    else
    {
      cur_player->weapons.insert(std::pair<mm_weapons::WEAPONS, int>((mm_weapons::WEAPONS)(i + 2), 0));
    }
  }
#ifdef DEBUG
  fprintf(stderr,"Load weapons OK.\n");
#endif

  camera.w = mm_graphs_defs::UTIL_W;
  camera.h = mm_graphs_defs::UTIL_H;

  std::string tiles_file = stage_path + "/tileset.bmp";
  loadTiles(tiles_file);

  return 0;
}

void Stage::createEnemies(std::vector<Character *> & characters_vec)
{
  for (int y = 0; y < max_y; ++y)
  {
    for (int x = 0; x < max_x; ++x)
    {
      unsigned char tile_action = map[y][x].action;
      Character * cur_char = NULL;
      if ((tile_action > mm_tile_actions::TILE_DEATH) && (tile_action < mm_tile_actions::TILE_TIMER))
      {
        int xpos, ypos;
        xpos = x * mm_graphs_defs::TILE_SIZE;
        ypos = y * mm_graphs_defs::TILE_SIZE;
        xpos += map[y][x].xOffset;
        if ((cur_char = createCharacter(tile_action, xpos, ypos)) != NULL)
        {
#ifdef DEBUG
          fprintf(stderr,"Enemy add: [%s]\n", mm_spritefiles::sprite_files[tile_action]);
#endif
          characters_vec.push_back(cur_char);
        }
      }
    }
  }
}

AnimSequence * Stage::getAnimSeq(unsigned int TYPE)
{
  return preLoadedSprites[TYPE];
}

BITMAP * Stage::getTileSet()
{
  return tileset;
}

void Stage::setOffset(int type, int offset)
{
  offsetMap.insert(std::pair<int,int>(type, offset));
}

int Stage::getOffset(int type)
{
  return (offsetMap[type]);
}

//inline void Stage::tileDraw(BITMAP * bmp, int tile_number, int x, int y)
//{
  //draw_sprite(bmp, tiles.tile_img[tile_number], x, y);
  //blit(tiles.tile_img[tile_number], bmp, 0,0, x,y, 32,32);
  //draw_rle_sprite(bmp, tiles.tile_img_rle[tile_number], x, y);
//}

void Stage::draw(BITMAP * bmp, const Camera & camera, bool hasFg, bool bg_only, bool bg)
{
  int i, j;
  int mapx, mapy;
  int map_xoff, map_yoff;

  mapx = camera.x / mm_graphs_defs::TILE_SIZE;
  mapy = camera.y / mm_graphs_defs::TILE_SIZE;

  map_xoff = camera.x & (mm_graphs_defs::TILE_SIZE-1);
  map_yoff = camera.y & (mm_graphs_defs::TILE_SIZE-1);

  int x,y;
  int xdisp = mapx + mm_graphs_defs::TILES_X==max_x ? 0 : 1;
  int ydisp = mapy + mm_graphs_defs::TILES_Y==max_y ? 0 : 1;

  xdisp += mm_graphs_defs::TILES_X;
  ydisp += mm_graphs_defs::TILES_Y;

  map_yoff = -map_yoff;
  map_xoff = -map_xoff;

  MAP_INFO * map_line;

  int tile_number = 0;

  if (hasFg == false)
  {
    y = map_yoff;
    for(i = 0; i < ydisp; ++i)
    {
      x = map_xoff;
      map_line = &map[mapy+i][mapx];
      for(j = 0; j < xdisp; ++j)
      {       
        tile_number = (*(map_line++)).tile_number;
        blit(tiles.tile_img[tile_number], bmp, 0,0, x,y, mm_graphs_defs::TILE_SIZE,mm_graphs_defs::TILE_SIZE);
        x += mm_graphs_defs::TILE_SIZE;
      }
      y += mm_graphs_defs::TILE_SIZE;
    }
  }
  else
  {
    if (bg_only == true)
    {
      y = map_yoff;
      for(i = 0; i < ydisp; ++i)
      {
        x = map_xoff;
        map_line = &map[mapy+i][mapx];
        for(j = 0; j < xdisp; ++j)
        {       
          tile_number = (*(map_line++)).tile_number;//map[mapy + i][mapx + j].tile_number;
          masked_blit(tiles.tile_img[tile_number], bmp, 0,0, x,y, mm_graphs_defs::TILE_SIZE,mm_graphs_defs::TILE_SIZE);
          x += mm_graphs_defs::TILE_SIZE;
        }
        y += mm_graphs_defs::TILE_SIZE;
      }
    }
    else
    {
      y = map_yoff;
      for(i = 0; i < ydisp; ++i) 
      {
        x = map_xoff;
        map_line = &map[mapy+i][mapx];
        for(j = 0; j < +xdisp; ++j)
        {
          if ((bg == false && map[mapy + i][mapx + j].isForeground == false) ||
              (bg == true  && map[mapy + i][mapx + j].isForeground == true))
          {
            x+=mm_graphs_defs::TILE_SIZE;
            ++map_line;
            continue;
          }

          tile_number = (*(map_line++)).tile_number;//map[mapy + i][mapx + j].tile_number;
          masked_blit(tiles.tile_img[tile_number], bmp, 0,0, x,y, mm_graphs_defs::TILE_SIZE,mm_graphs_defs::TILE_SIZE);
          x += mm_graphs_defs::TILE_SIZE;
        }
        y += mm_graphs_defs::TILE_SIZE;
      }
    }
  }
}

bool Stage::cameraSectorHasFgTiles(Camera & camera, Player & player)
{
  int camerax = player.x;// - mm_graphs_defs::UTIL_W/2;
  if (camerax < 0)
  {
    camerax = 0;
  }
  else if (camerax + camera.w >= (this->max_x*mm_graphs_defs::TILE_SIZE)) 
  {
    camerax = (this->max_x*mm_graphs_defs::TILE_SIZE)-camera.w;
  }

  int y = camera.y/mm_graphs_defs::TILE_SIZE;
  int x = camerax/mm_graphs_defs::TILE_SIZE;
  
  int ydesl = y / mm_graphs_defs::TILES_Y;
  int xdesl = x / mm_graphs_defs::TILES_X;
  int sector = ydesl*(this->max_x/mm_graphs_defs::TILES_X)+xdesl;

  return (sectors[sector].has_fg_tiles);
}

bool Stage::scrollForbid(Camera & camera, int camerax, int & forbid_dir)
{
  const int xline = (this->max_x/mm_graphs_defs::TILES_X);
  int y      = camera.y / mm_graphs_defs::TILE_SIZE;
  int x      = (camerax + camera.w) / mm_graphs_defs::TILE_SIZE;
  int x_left = (camerax - 1) / mm_graphs_defs::TILE_SIZE;

  if (x >= this->max_x) return true;
  if (x_left < 0)       return true;

  int ydesl  = y / mm_graphs_defs::TILES_Y;
  int xdesl  = x / mm_graphs_defs::TILES_X;
  int sector = ydesl*xline+xdesl;

  int xDeslLeft   = x_left / mm_graphs_defs::TILES_X;
  int left_sector = ydesl*xline+xDeslLeft;

  if (camerax > camera.old_x)
  {
    if (sectors[sector].scroll_forbid == true)
    {
      forbid_dir = 1;
      return true;
    }
  }
  else if (camerax <= camera.old_x)
  {
    if (sectors[left_sector].scroll_forbid == true)
    {
      forbid_dir = 0;
      return true;
    }
  }

  return false;
}

void Stage::doCamera(Player & player, Camera & camera)
{
  static int scroll_count = 0;
  static int dir = 0;

  int camerax = (player.x + mm_player_defs::HALFPLAYERWIDTH) - mm_graphs_defs::UTIL_W/2;
  if (camerax < 0)
  {
    camerax = 0;
  }
  else if (camerax + camera.w >= (this->max_x*mm_graphs_defs::TILE_SIZE)) 
  {
    camerax = (this->max_x*mm_graphs_defs::TILE_SIZE)-camera.w;
  }

  if (horz_scroll == false)
  {
    if (player.sy > mm_graphs_defs::UTIL_H)
    {
      horz_scroll = true;
      dir = 1;
    }
    else if (player.sy < 0 && camera.y != 0)
    {
      horz_scroll = true;
      dir = -1;
    }

    if (horz_scroll == true)
    {
      GlobalGameState::playerShots.clear();
      GlobalGameState::enemyShots.clear();
    }
  }
  else if (update_scroll == 1)
  {
    if (scroll_count < mm_graphs_defs::TILES_Y)
    {
      scroll_count++;
      camera.y += (dir*mm_graphs_defs::TILE_SIZE);
      player.y+=(dir*2);

      if (player.grabstair == true) player.handleAnimation();
    }
    else
    {
      horz_scroll = false;
      scroll_count = 0;
    }

    update_scroll = 0;
  }

  static bool reach_max_x = false;
  int forbid_dir;
  if (scrollForbid(camera, camerax, forbid_dir) == false)
  {
    reach_max_x = false;
    camera.old_x = camera.x;
    camera.x = camerax;
  }
  else
  {
    if (reach_max_x == false) 
    {
      camera.old_x = camera.x;

      if (forbid_dir == 1)
      {
        camera.x = camerax - camerax%mm_graphs_defs::TILE_SIZE;
      }
      else if (forbid_dir == 0)
      {
        // Could use abs(32-offset) and avoid the if..else whatever...
        int offset = camerax%mm_graphs_defs::TILE_SIZE;
        if (offset == 0)
          camera.x = camerax;
        else
          camera.x = camerax + (32 - offset);
      }

      reach_max_x = true;
    }
  }

  player.calcScreenCoords(camera);
}

void Stage::loadTiles(const std::string & stage_file)
{
  tileset = load_bitmap(stage_file.c_str(), NULL);

  int w,h;
  w = tileset->w - mm_graphs_defs::TILE_SIZE;
  h = tileset->h - mm_graphs_defs::TILE_SIZE;
  for (int y = 2; y < h; y+=mm_graphs_defs::TILE_SIZE+2)
  {
    for (int x = 2; x < w; x+=mm_graphs_defs::TILE_SIZE+2)
    {
      tiles.tile_img.push_back(create_sub_bitmap(tileset, x, y, mm_graphs_defs::TILE_SIZE, mm_graphs_defs::TILE_SIZE));
      tiles.x_pos.push_back(0);
    }
  }
}

Character * Stage::createCharacter(unsigned int TYPE, int x, int y, int vx, int vy, void * param)
{
  Character * cur_character = NULL;

  switch(TYPE)
  {
    case mm_tile_actions::TILE_ENEMY_BLADER:
    {
      cur_character = new Blader(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_BEAK:
    {
      cur_character = new Beak(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_SUPERCUTTER:
    {
      cur_character = new SuperCutter(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_FLYINGSHELL:
    {
      cur_character = new FlyingShell(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_KILLERBULLET:
    {
      cur_character = new KillerBullet(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_SPINE:
    {
      cur_character = new Spine(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_MET:
    {
      cur_character = new Met(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_PICKETMAN:
    {
      cur_character = new PicketMan(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_SNIPERJOE:
    {
      cur_character = new SniperJoe(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_PENG:
    {
      cur_character = new Peng(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_BIGEYE:
    {
      cur_character = new BigEye(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_FLEA:
    {
      cur_character = new Flea(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_BOMBOMB:
    {
      cur_character = new Bombomb(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_OCTOPUSBATTERY_V:
    {
      cur_character = new OctopusBattery(*this, x, y, OctopusBattery::VERTICAL);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_OCTOPUSBATTERY_H:
    {
      cur_character = new OctopusBattery(*this, x, y, OctopusBattery::HORIZONTAL);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_SCREWBOMBER:
    {
      cur_character = new ScrewBomber(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_ENEMY_CRAZYRAZY:
    {
      cur_character = new CrazyRazy(*this, x, y);
    }
    break;
    case mm_tile_actions::TILE_MOVING_PLATFORM:
    {
      cur_character = new MovePlatform(*this, x, y);
    }
    break;
    case mm_tile_actions::HIT_EXPLOSION_CHAR:
    {
      cur_character = new HitExplosion(*this, x, y);
    }
    break;
    case mm_tile_actions::EXPLOSION_LITTLE_CHAR:
    {
      cur_character = new ExplosionLittle(*this, x, y);
    }
    break;
    case mm_tile_actions::MEGAMAN_EXPLOSION_CHAR:
    {
      cur_character = new MegamanExplosion(*this, x, y, param);
      cur_character->velx = vx;
      cur_character->vely = vy;
    }
    break;
    case mm_tile_actions::BOMBOMB_FRAGMENT_CHAR:
    {
      cur_character = new Bombomb::BombombFragment(*this, x, y);
      cur_character->velx = vx;
      cur_character->vely = vy;
    }
    break;
    case mm_tile_actions::PICKETMAN_HAMMER_CHAR:
    {
      cur_character = new PicketMan::PicketManHammer(*this, x, y);
      cur_character->velx = vx;
      cur_character->vely = vy;
    }
    break;
    case mm_tile_actions::CRAZYRAZY_LOWER_CHAR:
    {
      cur_character = new CrazyRazy::CrazyRazyFragmentLowerHalf(*this, x, y);
      cur_character->velx = vx;
    }
    break;
    case mm_tile_actions::CRAZYRAZY_UPPER_CHAR:
    {
      cur_character = new CrazyRazy::CrazyRazyFragmentUpperHalf(*this, x, y);
    }
    break;
  }

  return (cur_character);
}
